﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Data.OleDb;

namespace 七龙纪II
{
    class VerifyCode
    {
        /// <summary>
        /// 进行彩色去噪点
        /// </summary>
        /// <param name="image">原位图</param>
        /// <returns>处理后位图</returns>
        public static Bitmap colorClear(Bitmap image)
        {
            //滤波计算
            //计算该像素点周围8个点及本身的RGB平均值，
            //计算这9个点到平均值的欧式距离。
            //从计算中选取最小的，用其RBG值代替该点的RGB值
            Color currentPixel;
            Bitmap imageResult = new Bitmap(image);
            for (int x = 0; x < image.Width; x++)
            {
                for (int y = 0; y < image.Height; y++)
                {
                    //取包括本身在内的9个点。并且计算其平均值
                    int Red = 0, Blue = 0, Green = 0;
                    int PointCount = 0;
                    for (int i = -1; i <= 1; i++)
                    {
                        for (int j = -1; j <= 1; j++)
                        {
                            //有像素点的话，叠加
                            if (x + i >= 0 && y + j >= 0 && x + i < image.Width && y + j < image.Height)
                            {
                                currentPixel = image.GetPixel(x, y);
                                Red += Convert.ToInt32(currentPixel.R);
                                Blue += Convert.ToInt32(currentPixel.B);
                                Green += Convert.ToInt32(currentPixel.G);
                                PointCount++;
                            }
                        }
                    }
                    Red = Red / PointCount;
                    Blue = Blue / PointCount;
                    Green = Green / PointCount;
                    //计算最小的欧式距离
                    double iDistance = 9999999; //默认无限大
                    int iX = x, iY = y; //欧式距离最小的那个色点，默认为本身
                    for (int i = -1; i <= 1; i++)
                    {
                        for (int j = -1; j <= 1; j++)
                        {
                            //有像素点的话，叠加
                            if (x + i >= 0 && y + j >= 0 && x + i < image.Width && y + j < image.Height)
                            {
                                currentPixel = image.GetPixel(x + i, y + j);
                                double iCurrentDistance = Math.Pow((Convert.ToDouble(currentPixel.R) - Red), 2) + Math.Pow(Convert.ToDouble(currentPixel.G) - Green, 2) + Math.Pow(Convert.ToDouble(currentPixel.B) - Blue, 2);
                                if (iCurrentDistance < iDistance)
                                {
                                    iX = x + i;
                                    iY = y + j;
                                    iDistance = iCurrentDistance;
                                }
                            }
                        }
                    }
                    imageResult.SetPixel(x, y, image.GetPixel(iX, iY));
                }
            }
            return imageResult;
        }
        /// <summary>
        /// 以Argb数值进行过滤
        /// </summary>
        /// <param name="img">源图像</param>
        /// <param name="x">argb数值</param>
        /// <returns></returns>
        public static Image graylevel(Image img, int x)
        {
            Bitmap Img = new Bitmap(img);
            for (int w = 0; w <= Img.Width - 1; w++)
            {
                for (int h = 0; h <= Img.Height - 1; h++)
                {
                    //否则就转化成白色
                    Color c = Img.GetPixel(w, h);
                    if (c.ToArgb() > x)
                        Img.SetPixel(w, h, Color.White);
                }
            }
            return Img;
        }
        /// <summary>
        /// 转化为灰度图像，进行二值化处理
        /// </summary>
        /// <param name="image">源图像</param>
        /// <param name="iThreshold">阈值</param>
        /// <returns>处理后</returns>
        public static Bitmap toGrey(Bitmap imageSrc, int iThreshold)
        {
            //函数将RGB三色表示转换为单比特的0或者1表示
            //利用公式 Grey=0.3*red+0.59*green+0.11*blue计算出灰度值
            //大于阈值iThreshold的就放为黑色，小于的话就为白色
            Color color;
            Bitmap image = new Bitmap(imageSrc);
            for (int x = 0; x < image.Width; x++)
            {
                for (int y = 0; y < image.Height; y++)
                {
                    color = image.GetPixel(x, y);
                    double iGrey = 0.3 * Convert.ToInt32(color.R) + 0.59 * Convert.ToInt32(color.G) + 0.11 * Convert.ToInt32(color.B);
                    if (iGrey < iThreshold)
                    {
                        image.SetPixel(x, y, Color.Black);
                    }
                    else
                    {
                        image.SetPixel(x, y, Color.White);
                    }
                }
            }
            return image;
        }
        /// <summary>
        /// 黑白去噪
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <param name="iPointWidth">像素宽度</param>
        /// <param name="iThreshold">阈值</param>
        /// <returns>处理后图像</returns>
        public static Bitmap blackClear(Bitmap imageSrc, int iPointWidth, int iThreshold)
        {
            //检查像素点，如果为白色0，跳过，如果为黑色1则进行噪点处理
            //统计周围的像素点，如果白色的个数超过阈值，则认为是噪点，将其设置为白色。
            Color color;
            Bitmap image = new Bitmap(imageSrc.Width + 2 * iPointWidth, imageSrc.Height + 2 * iPointWidth);
            //设置为全白，并拷贝图像
            Graphics g = Graphics.FromImage(image);
            g.Clear(Color.White);
            g.DrawImage(imageSrc, iPointWidth, iPointWidth);
            g.Dispose();
            Bitmap imageResult = new Bitmap(image);

            for (int x = iPointWidth - 1; x < image.Width - iPointWidth + 1; x++)
            {
                for (int y = iPointWidth - 1; y < image.Height - iPointWidth + 1; y++)
                {

                    color = image.GetPixel(x, y);
                    int iWhiteCount = 0;
                    if (color.ToArgb() == Color.Black.ToArgb()) //黑色的话进行检查边上的。注意颜色对象不能直接比较。必须转换一下
                    {
                        for (int i = -iPointWidth; i <= iPointWidth; i++)
                        {
                            for (int j = -iPointWidth; j <= iPointWidth; j++)
                            {
                                //有像素点的话，统计
                                if (x + i >= 0 && y + j >= 0 && x + i < image.Width && y + j < image.Height)
                                {
                                    //该边际点为白色，且不是自身
                                    if (image.GetPixel(x + i, y + j).ToArgb() == Color.White.ToArgb() && i != 0 && j != 0)
                                    {
                                        iWhiteCount++;
                                    }
                                }
                            }
                        }
                        if (iWhiteCount > iThreshold) //白色点数大于阈值，认为是噪点
                        {
                            imageResult.SetPixel(x, y, Color.White);
                        }

                    }
                }
            }
            image.Dispose();
            return imageResult;
        }
        /// <summary>
        /// 显示各列的像素分部情况
        /// </summary>
        /// <param name="imageShow">显示的地方</param>
        /// <param name="imageSrc">要显示的图片</param>
        /// <returns>数组方式返回各点</returns>
        public static int[] pointShow(ref Bitmap imageShow, Bitmap imageSrc)
        {
            int[] iPointCount = new int[imageSrc.Width];
            Graphics g = Graphics.FromImage(imageShow);
            g.Clear(Color.White);
            g.Dispose();
            float iWidthBand = (float)imageShow.Width / imageSrc.Width;
            float iHeightBand = (float)imageShow.Height / imageSrc.Height;
            for (int x = 0; x < imageSrc.Width; x++)
            {
                int iBlackCount = 0;
                //映射得到黑色的点数
                for (int y = 0; y < imageSrc.Height; y++)
                {
                    if (imageSrc.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
                        iBlackCount++;
                }
                int iX = Convert.ToInt32(x * iWidthBand);
                int iY = Convert.ToInt32(iBlackCount * iHeightBand);
                iPointCount[x] = iBlackCount;
                for (int y = 1; y < imageShow.Height && y < iY; y++)
                {
                    imageShow.SetPixel(iX, y, Color.Black);
                }

            }
            return iPointCount;
        }
        /// <summary>
        /// 切割图片
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <param name="minWidth">最少宽度</param>
        /// <returns></returns>
        public static ClipImage imageSplit(Bitmap imageSrc, int minWidth, int splitThreshold)
        {

            ClipImage clipImage = new ClipImage();
            clipImage.ImagePreView = new Bitmap(imageSrc);
            clipImage.ClipImages = new Bitmap[10];
            Graphics g = Graphics.FromImage(clipImage.ImagePreView);
            Pen pen = new Pen(Brushes.Red);

            int startX = 1; //开始切割的地方
            int endX = 1; //结束的地方
            int startY = 1;//纵向开始
            int endY = 1;//纵向结束
            bool isChar = false; //是一个验证码字符
            int iImageIndex = 0;
            //获取各个列的黑色点数
            for (int x = 0; x < imageSrc.Width; x++)
            {
                int iBlackCount = 0;
                //映射得到黑色的点数
                for (int y = 0; y < imageSrc.Height; y++)
                {
                    if (imageSrc.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
                        iBlackCount++;
                }
                if (iBlackCount < splitThreshold) //左边的黑色点数少于预定数，变成红色
                {
                    g.DrawLine(pen, x, 0, x, imageSrc.Height);
                    endX = x; //当前为结束点
                    if (isChar == true && endX - startX >= minWidth)
                    {
                        //这个字符找到了,纵向切割。上下两头逼近
                        #region 找到字符，上下两头逼近
                        int iCount = 0;
                        for (int y = 1; y < imageSrc.Height - 1; y++)
                        {
                            iCount = 0;
                            for (int i = startX; i <= endX; i++)
                            {
                                if (imageSrc.GetPixel(i, y).ToArgb() == Color.Black.ToArgb())
                                    iCount++;
                            }
                            //如果像素点超过了2个，那么就设置为开始，退出循环
                            if (iCount > splitThreshold)
                            {
                                startY = y;
                                break;
                            }
                            //否则画红线
                            else
                            {
                                g.DrawLine(pen, startX, y, endX, y);
                            }

                        }
                        for (int y = imageSrc.Height - 2; y > 0; y--)
                        {
                            iCount = 0;
                            for (int i = startX; i <= endX; i++)
                            {
                                if (imageSrc.GetPixel(i, y).ToArgb() == Color.Black.ToArgb())
                                    iCount++;
                            }
                            //如果像素点超过了2个，那么就设置为开始，退出循环
                            if (iCount > splitThreshold)
                            {
                                endY = y;
                                break;
                            }
                            //否则画红线
                            else
                            {
                                g.DrawLine(pen, startX, y, endX, y);
                            }

                        }
                        #endregion
                        Rectangle rec = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1);
                        clipImage.ClipImages[iImageIndex] = imageSrc.Clone(rec, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                        iImageIndex++;
                        isChar = false;
                        startX = endX;
                    }
                    else
                    {
                        startX = x;
                    }
                }
                else
                {
                    endX = x;
                    isChar = true;
                }
            }
            return clipImage;
        }
        /// <summary>
        /// 将数据存入数据库，以|符号间隔
        /// </summary>
        /// <param name="sConnStr">数据库链接字符串</param>
        /// <param name="sVerifyURL">验证码来源地址</param>
        public static void saveImage(Bitmap imageSrc, string sCode, string sConnStr, string sVerifyURL)
        {
            //首先将验证码处理成字符串黑色为1，
            //将字符串，长度，宽度存入数据库
            OleDbConnection odcConnection = new OleDbConnection(sConnStr);
            odcConnection.Open();
            OleDbCommand odCommand = odcConnection.CreateCommand();
            odCommand.CommandText = "select VerifyID from Verify where VerifyURL='" + sVerifyURL + "'";
            object sVerifyID = odCommand.ExecuteScalar();

            if (sVerifyID == null) //如果没有找到。
            {
                odCommand.CommandText = "insert into Verify(VerifyURL) values('" + sVerifyURL + "')";
                odCommand.ExecuteNonQuery();
                odCommand.CommandText = "select VerifyID from Verify where VerifyURL='" + sVerifyURL + "'";
                sVerifyID = odCommand.ExecuteScalar().ToString();
            }
            string sImageStr = imageToString(imageSrc);
            odCommand.CommandText = "insert into Model(VerifyID,Code,[Data]) values(" + sVerifyID + ",'" + sCode + "','" + sImageStr + "')";
            odCommand.ExecuteNonQuery();
            odcConnection.Dispose();
            odCommand.Dispose();
        }
        /// <summary>
        /// 将字符图像转换为字符串
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <returns></returns>
        public static string imageToString(Bitmap imageSrc)
        {
            string sReturnValue = "";
            for (int y = 0; y < imageSrc.Height; y++)
            {
                int iLine = 0;
                for (int x = 0; x < imageSrc.Width; x++)
                {
                    if (imageSrc.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
                    {
                        iLine = iLine << 1 | 1;
                    }
                    else
                    {
                        iLine = iLine << 1 | 0;
                    }

                }
                sReturnValue += iLine.ToString() + "|";
            }
            return sReturnValue.TrimEnd('|');
        }
        /// <summary>
        /// 从数据库中检索数据并比较
        /// </summary>
        /// <param name="imageSrc"></param>
        /// <param name="sConnStr"></param>
        /// <param name="sVerifyURL"></param>
        /// <returns></returns>
        public static VerifyResult searchCode(Bitmap imageSrc, string sConnStr, string sVerifyURL)
        {
            OleDbConnection odcConnection = new OleDbConnection(sConnStr);
            OleDbDataReader odrReader;
            //验证码检索返回结果
            VerifyResult verifyResult = new VerifyResult();
            verifyResult.Likeness = 0;
            verifyResult.Code = "*";
            int[] iDataSrc = stringToInt(imageToString(imageSrc)); //图像装换为整型数组
            odcConnection.Open();
            OleDbCommand odCommand = odcConnection.CreateCommand();
            odCommand.CommandText = "select Code,[Data] from Model inner join Verify on Verify.VerifyID=Model.VerifyID where  VerifyURL='" + sVerifyURL + "'";
            odrReader = odCommand.ExecuteReader();
            while (odrReader.Read())
            {
                int[] iDataNow = stringToInt(odrReader["Data"].ToString());
                double dLikeTemp = GetCosine(iDataNow, iDataSrc, imageSrc.Width, imageSrc.Height);
                if (dLikeTemp > 0.8 && dLikeTemp > verifyResult.Likeness)
                {
                    verifyResult.Code = odrReader["Code"].ToString();
                    verifyResult.Likeness = dLikeTemp;
                }
            }
            odrReader.Close();
            odrReader.Dispose();
            odcConnection.Dispose();
            odCommand.Dispose();
            return verifyResult;
        }
        /// <summary>
        /// 字符串装换为整型数组
        /// </summary>
        /// <param name="sImageData"></param>
        /// <returns></returns>
        public static int[] stringToInt(string sImageData)
        {
            string[] sLines = sImageData.Split('|');
            int[] iReturnValue = new int[sLines.Length];
            for (int i = 0; i < sLines.Length; i++)
                iReturnValue[i] = Convert.ToInt32(sLines[i]);
            return iReturnValue;
        }
        /// <summary>
        /// 比较两个数字的相似度
        /// </summary>
        /// <param name="e1"></param>
        /// <param name="e2"></param>
        /// <returns></returns>
        public static double GetCosine(int[] e1, int[] e2, int width, int height)
        {
            int a = 0;//分母1
            int b = 0;//分母2
            int c = 0;//分子
            for (int y = 0; y < height; ++y)
            {
                //两个数组中的整数按位与
                int i = e2[y] & e1[y];
                //按位加
                for (int x = 0; x < width; ++x)
                {
                    c += (i >> x) & 1;
                    a += (e2[y] >> x) & 1;
                    b += (e1[y] >> x) & 1;
                }
            }

            //计算分母
            int d = a * b;

            return d == 0 ? 0 : c / Math.Sqrt(d);
        }
        /// <summary>
        /// 采用双线性内插值算法，缩放图像到31*31
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <returns></returns>
        public static Bitmap reSizeImage(Bitmap imageSrc)
        {
            /*双线性内插值：
             * 对于一个目的像素，设置坐标通过反向变换得到的浮点坐标为(i+u,j+v)，其中i、j均为非负整数，u、v为[0,1)区间的浮点数，
             * 则这个像素得值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定，即：
             * f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)
             * 其中f(i,j)表示源图像(i,j)处的的像素值，以此类推
            */
            Bitmap image = new Bitmap(31, 31);
            double dX = (float)image.Width / (float)imageSrc.Width; //宽度倍数
            double dY = (float)image.Height / (float)imageSrc.Height;//高度倍数
            int i, j;
            double u, v;
            double result;
            Color color;
            for (int x = 0; x < image.Width; x++)
            {
                for (int y = 0; y < image.Height; y++)
                {
                    i = (int)Math.Floor(x / dX);
                    u = x / dX - i;
                    j = (int)Math.Floor(y / dY);
                    v = y / dY - j;
                    result = (1 - u) * (1 - v) * getPixelValue(imageSrc, i, j) + (1 - u) * v * getPixelValue(imageSrc, i, j + 1) + u * (1 - v) * getPixelValue(imageSrc, i + 1, j) + u * v * getPixelValue(imageSrc, i + 1, j + 1);
                    color = result > 0.5 ? Color.Black : Color.White;
                    image.SetPixel(x, y, color);
                }
            }
            return image;
        }
        /// <summary>
        /// 获得图像某个点的值，黑色为1，白色为0
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <param name="x">横坐标</param>
        /// <param name="y">纵坐标</param>
        /// <returns></returns>
        public static int getPixelValue(Bitmap imageSrc, int x, int y)
        {
            int iReturnValue = 0;
            if (x < imageSrc.Width && y < imageSrc.Height)
            {
                iReturnValue = imageSrc.GetPixel(x, y).ToArgb() == Color.Black.ToArgb() ? 1 : 0;
            }
            return iReturnValue;
        }
        /// <summary>
        /// 去除边框
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <param name="iBorderWidth">边框</param>
        /// <returns></returns>
        public static Bitmap deleteBorder(Bitmap imageSrc, int iBorderWidth)
        {
            Bitmap image = new Bitmap(imageSrc.Width - 2 * iBorderWidth, imageSrc.Height - 2 * iBorderWidth);
            //复制边框以外的图像到新图像
            Rectangle rec = new Rectangle(iBorderWidth, iBorderWidth, imageSrc.Width - iBorderWidth * 2, imageSrc.Height - iBorderWidth * 2);
            image = imageSrc.Clone(rec, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            return image;
        }
        /// <summary>
        /// 用燃烧法进行图像遍历
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <param name="iMinPercent">比例</param>
        /// <returns></returns>
        public static ClipImage imageSplit(Bitmap imageSrc, int iMinPercent)
        {
            ClipImage clipImage = new ClipImage();
            Bitmap image = new Bitmap(imageSrc);
            clipImage.ClipImages = new Bitmap[10];
            int iImageOrder = 0;//图片序号
            int iPointMin = iMinPercent * imageSrc.Height * imageSrc.Width / 100;//符合要求的像素点数
            //初始化扫描结果
            SearchResult searchResult = new SearchResult();
            searchResult.Image = new Bitmap(imageSrc.Width, imageSrc.Height);
            //全部填充为白色
            Graphics g = Graphics.FromImage(searchResult.Image);
            g.Clear(Color.White);

            //纵向扫描过去
            for (int x = 0; x < imageSrc.Width; x++)
            {
                for (int y = 0; y < imageSrc.Height; y++)
                {
                    if (imageSrc.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
                    {
                        //从这点开始遍历检索
                        searchResult.pointCount = 0; //连续黑色点数
                        searchResult.XCurrent = x; //当前X坐标
                        searchResult.XEnd = x; //结束X坐标
                        searchResult.XStart = x;//开始X坐标
                        searchResult.YCurrent = y; //当前X坐标
                        searchResult.YEnd = y; //结束Y坐标
                        searchResult.YStart = y; //开始Y坐标
                        searchImage(ref image, ref searchResult);

                        if (searchResult.pointCount > iPointMin)
                        {
                            //建立一个图像
                            clipImage.ClipImages[iImageOrder] = new Bitmap(searchResult.XEnd - searchResult.XStart + 1, searchResult.YEnd - searchResult.YStart + 1);
                            //复制保存起来
                            Rectangle rec = new Rectangle(searchResult.XStart, searchResult.YStart, searchResult.XEnd - searchResult.XStart + 1, searchResult.YEnd - searchResult.YStart + 1);
                            clipImage.ClipImages[iImageOrder] = searchResult.Image.Clone(rec, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                            iImageOrder++;
                            //将图像再次涂白。
                            g.Clear(Color.White);
                        }
                    }
                }
            }
            g.Dispose();
            clipImage.ImagePreView = image;
            return clipImage;
        }
        /// <summary>
        /// 检索图像
        /// </summary>
        /// <param name="imageSrc">源图像</param>
        /// <param name="searchResult">结果</param>
        public static void searchImage(ref Bitmap imageSrc, ref SearchResult searchResult)
        {
            //设置源图为红色，表示已遍历到
            imageSrc.SetPixel(searchResult.XCurrent, searchResult.YCurrent, Color.Red);
            //设置结果为黑色，存储
            searchResult.Image.SetPixel(searchResult.XCurrent, searchResult.YCurrent, Color.Black);
            //有效色素
            searchResult.pointCount++;
            //校准图像坐标范围
            searchResult.XStart = searchResult.XStart < searchResult.XCurrent ? searchResult.XStart : searchResult.XCurrent;
            searchResult.YStart = searchResult.YStart < searchResult.YCurrent ? searchResult.YStart : searchResult.YCurrent;
            searchResult.XEnd = searchResult.XEnd > searchResult.XCurrent ? searchResult.XEnd : searchResult.XCurrent;
            searchResult.YEnd = searchResult.YEnd > searchResult.YCurrent ? searchResult.YEnd : searchResult.YCurrent;
            //把当前的位置保存下来，因为searchResult是引用传递，数值会变化
            int iCurrentX = searchResult.XCurrent;
            int iCurrentY = searchResult.YCurrent;
            for (int i = -1; i <= 1; i++)
            {
                for (int j = -1; j <= 1; j++)
                {
                    if (iCurrentX + i >= 0 && iCurrentY + j >= 0 && iCurrentX + i < imageSrc.Width && iCurrentY + j < imageSrc.Height)
                    {
                        //边上的8个点，如果为黑色，进入那个点遍历。
                        if (Math.Abs(i + j) == 1 && imageSrc.GetPixel(iCurrentX + i, iCurrentY + j).ToArgb() == Color.Black.ToArgb()) //如果那个点为黑色
                        {
                            searchResult.XCurrent = iCurrentX + i;
                            searchResult.YCurrent = iCurrentY + j;
                            searchImage(ref imageSrc, ref searchResult);
                        }

                    }
                }
            }
        }
    }
}
